/*****************************************************************************
 * mqtt_mongoose.c
 *
 * Copyright (C) 2020 Jeasonvor 1101627719@qq.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"
#include "thirdparty/mongoose/mongoose.h"

/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_INFO
#define LOG_MODULE    "mqtt.mongoose"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define CAL_MQTT_MALLOC(x) malloc(x)  // plum_kal_mem_malloc(x)
#define CAL_MQTT_FREE(ptr) free(ptr)  // plum_kal_mem_free(ptr)

/*****************************************************************************
 * Private Types
 ****************************************************************************/

typedef struct {
    struct mg_mgr mgr;

    plum_s8 *client_id;
    plum_s8 *url;
    plum_s8 *user;
    plum_s8 *passwd;

    plum_mqtt_cb cb;
} cal_mqtt_client_t;

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/
PLUM_PRIVATE
cal_mqtt_client_t client;
/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

plum_s32 cal_mqtt_client_regster(plum_s8 *client_id, plum_s8 *uri,
                                 plum_u32 port, plum_s8 *usr, plum_s8 *passwd)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        client.client_id = CAL_MQTT_MALLOC(strlen(client_id));
        if (!client.client_id) {
            LOG_E("malloc client id err");
            rc = PLUM_ECODE_EMEM;
            break;
        }
        snprintf(client.client_id, strlen(client_id), "%s", client_id);

        client.url = CAL_MQTT_MALLOC(strlen(uri) + 6);
        if (!client.url) {
            LOG_E("malloc url err");
            rc = PLUM_ECODE_EMEM;
            break;
        }
        snprintf(client.url, strlen(uri) + 6, "%s:%d", uri, port);

        client.user = CAL_MQTT_MALLOC(strlen(usr));
        if (!client.user) {
            LOG_E("malloc user err");
            rc = PLUM_ECODE_EMEM;
            break;
        }
        snprintf(client.user, strlen(usr), "%s", usr);

        client.passwd = CAL_MQTT_MALLOC(strlen(passwd));
        if (!client.passwd) {
            LOG_E("malloc passwd err");
            rc = PLUM_ECODE_EMEM;
            break;
        }
        snprintf(client.passwd, strlen(passwd), "%s", passwd);
    } while (0);

    return (rc);
}

PLUM_PRIVATE
plum_void cal_mqtt_fn(struct mg_connection *c, plum_s32 ev, plum_void *ev_data,
                      plum_void *fn_data)
{
    if (ev == MG_EV_OPEN) {
        LOG_I("MG_EV_OPEN");
        // c->is_hexdumping = 1;
    }
    else if (ev == MG_EV_ERROR) {
        LOG_I("MG_EV_ERROR");
        // On error, log error message
        MG_ERROR(("%p %s", c->fd, (char *)ev_data));
    }
    else if (ev == MG_EV_CONNECT) {
        LOG_I("MG_EV_CONNECT");
        // If target URL is SSL/TLS, command client connection to use TLS
        if (mg_url_is_ssl(client.url)) {
            struct mg_tls_opts opts = {.ca = "ca.pem"};
            mg_tls_init(c, &opts);
        }
    }
    else if (ev == MG_EV_MQTT_OPEN) {
        // MQTT connect is successful
        LOG_I("MG_EV_MQTT_OPEN");

#if 0
    struct mg_str subt = mg_str(s_sub_topic);
    struct mg_str pubt = mg_str(s_pub_topic), data = mg_str("hello");
    MG_INFO(("CONNECTED to %s", s_url));
    mg_mqtt_sub(c, subt, s_qos);
    MG_INFO(("SUBSCRIBED to %.*s", (int) subt.len, subt.ptr));

    mg_mqtt_pub(c, pubt, data, s_qos, false);
    MG_INFO(("PUBSLISHED %.*s -> %.*s", (int) data.len, data.ptr,
             (int) pubt.len, pubt.ptr));
#endif
    }
    else if (ev == MG_EV_MQTT_MSG) {
        LOG_I("MG_EV_MQTT_MSG");
        // When we get echo response, print it
        struct mg_mqtt_message *mm = (struct mg_mqtt_message *)ev_data;
        MG_INFO(("RECEIVED %.*s <- %.*s", (int)mm->data.len, mm->data.ptr,
                 (int)mm->topic.len, mm->topic.ptr));
        c->is_closing = 1;
    }
    else if (ev == MG_EV_CLOSE) {
        LOG_I(("CLOSED"));
        struct mg_mqtt_opts opts = {
            .clean     = true,
            .keepalive = 60,
            .client_id = mg_str(client.client_id),
            .user      = mg_str(client.user),
            .pass      = mg_str(client.passwd),
        };
        mg_mqtt_connect(&client.mgr, client.url, &opts, cal_mqtt_fn, NULL);
    }
    (void)fn_data;
}

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_mqtt_client_ref_t plum_cal_mqtt_connect(plum_s8 *client_id, plum_s8 *url,
                                             plum_u32 port, plum_s8 *usr,
                                             plum_s8 *passwd)
{
    plum_mqtt_client_ref_t handle;

    do {
        mg_mgr_init(&client.mgr);  // Init event manager

        struct mg_mqtt_opts opts = {
            .clean     = true,
            .keepalive = 60,
            .client_id = mg_str(client_id),
            .user      = mg_str(usr),
            .pass      = mg_str(passwd),
        };
        plum_s8 URL[64] = {0};
        snprintf(URL, sizeof(URL), "%s:%d", url, port);
        handle = mg_mqtt_connect(&client.mgr, URL, &opts, cal_mqtt_fn, NULL);
        if (!handle) {
            LOG_E("mg_mqtt_connect err");
            break;
        }

        mg_mgr_poll(&client.mgr, 1000);  // Event loop, 1s timeout
        mg_mgr_free(&client.mgr);        // Finished, cleanup

        cal_mqtt_client_regster(client_id, url, port, usr, passwd);
    } while (0);

    return (handle);
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_disconnect(plum_mqtt_client_ref_t client)
{
    plum_s32 rc = PLUM_ECODE_OK;

    struct mg_connection *hdl = (struct mg_connection *)client;
    if (!hdl) {
        LOG_E("mqtt client pointer is null");
        rc = PLUM_ECODE_INVALID;
    }
    else {
        mg_mqtt_disconnect(hdl);
    }

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_subscribe(plum_mqtt_client_ref_t client, plum_s8 *topic)
{
    plum_s32 rc = PLUM_ECODE_OK;

    struct mg_connection *hdl = (struct mg_connection *)client;
    if (!hdl) {
        LOG_E("mqtt client pointer is null");
        rc = PLUM_ECODE_INVALID;
    }
    else {
        mg_mqtt_sub(hdl, mg_str(topic), 1);
    }

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_unsubscribe(plum_mqtt_client_ref_t client,
                                   plum_s8 *              topic)
{
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_publish(plum_mqtt_client_ref_t client,
                               plum_mqtt_msg_t *      msg)
{
    plum_s32 rc = PLUM_ECODE_OK;

    struct mg_connection *hdl = (struct mg_connection *)client;
    if (!hdl) {
        LOG_E("mqtt client pointer is null");
        rc = PLUM_ECODE_INVALID;
    }
    else {
        mg_mqtt_pub(hdl, mg_str(msg->topic), mg_str(msg->payload), 1, false);
    }

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_register_callback(plum_mqtt_cb cb)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!cb) {
            LOG_E("");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        client.cb = cb;
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_cal_mqtt_register_drv_io(plum_mqtt_drv_read  read,
                                       plum_mqtt_drv_write write)
{
}

PLUM_PUBLIC
plum_void plum_cal_mqtt_yield(plum_void)
{
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
